package calcs;

import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
import static util.UserInputChecker.checkInput;
import static util.UserInputChecker.setCalculated;
import static util.UserInputChecker.setInitial;
import static util.Util.roundToDecimals;
import gui.ErrorMessage;
import gui.MyFrame;
import gui.data.D3.SphereOutputGui;

public class SphereCalculator extends Thread {
	double volume, area, circumference, radius, capVolume, capArea,
			capCircumference, capRadius, capHeight;
	double pi = Math.PI;
	boolean found = false, before = false, calculated = false;
	MyFrame myFrame;
	int n = 0;

	public SphereCalculator(double volume, double area, double circumference,
			double radius, double capVolume, double capArea,
			double capCircumference, double capRadius, double capHeight,
			final MyFrame myFrame) {
		this.volume = volume;
		this.area = area;
		this.circumference = circumference;
		this.radius = radius;
		this.capVolume = capVolume;
		this.capArea = capArea;
		this.capCircumference = capCircumference;
		this.capRadius = capRadius;
		this.capHeight = capHeight;
		this.myFrame = myFrame;
		setInitial(volume, area, circumference, radius, capVolume, capArea,
				capCircumference, capRadius, capHeight);
		start();
	}

	public SphereCalculator(double volume, double area, double circumference,
			double radius, double capVolume, double capArea,
			double capCircumference, double capRadius, double capHeight) {
		this.volume = volume;
		this.area = area;
		this.circumference = circumference;
		this.radius = radius;
		this.capVolume = capVolume;
		this.capArea = capArea;
		this.capCircumference = capCircumference;
		this.capRadius = capRadius;
		this.capHeight = capHeight;
	}

	public void run() {
		do {
			found = false;
			before = false;
			calculated = false;

			before = volume != 0;
			calculateVolume();
			if (!before && found) {
				calculated = true;
			}

			before = area != 0;
			calculateArea();
			if (!before && found) {
				calculated = true;
			}

			before = circumference != 0;
			calculateCircumference();
			if (!before && found) {
				calculated = true;
			}

			before = radius != 0;
			calculateRadius();
			if (!before && found) {
				calculated = true;
			}

			before = capVolume != 0;
			calculateCapVolume();
			if (!before && found) {
				calculated = true;
			}

			before = capArea != 0;
			calculateCapArea();
			if (!before && found) {
				calculated = true;
			}

			before = capCircumference != 0;
			calculateCapCircumference();
			if (!before && found) {
				calculated = true;
			}

			before = capRadius != 0;
			calculateCapRadius();
			if (!before && found) {
				calculated = true;
			}

			before = capHeight != 0;
			calculateCapHeight();
			if (!before && found) {
				calculated = true;
			}
			n++;
		} while (calculated && n <= 100);
		finish();
	}

	private void finish() {
		volume = roundToDecimals(volume, 2);
		area = roundToDecimals(area, 2);
		circumference = roundToDecimals(circumference, 2);
		radius = roundToDecimals(radius, 2);
		capVolume = roundToDecimals(capVolume, 2);
		capArea = roundToDecimals(capArea, 2);
		capCircumference = roundToDecimals(capCircumference, 2);
		capRadius = roundToDecimals(capRadius, 2);
		capHeight = roundToDecimals(capHeight, 2);
		if (myFrame != null) {
			setCalculated(volume, area, circumference, radius, capVolume,
					capArea, capCircumference, capRadius, capHeight);
			if (checkInput()) {
				myFrame.setDataPanel(new SphereOutputGui(volume, area,
						circumference, radius, capVolume, capArea,
						capCircumference, capRadius, capHeight),
						"SPHERE_OUTPUT");
			} else {
				new ErrorMessage("Check input");
			}
		}
	}

	private void calculateCapHeight() {
		if (radius != 0 && capRadius != 0) {
			capHeight = radius - sqrt(pow(radius, 2) - pow(capRadius, 2));
			found = true;
		} else if (capArea != 0) {
			if (radius != 0) {
				capHeight = capArea / (2 * pi * radius);
				found = true;
			} else if (capRadius != 0) {
				capHeight = sqrt((capArea - (pi * pow(capRadius, 2))) / pi);
				found = true;
			}
		}
	}

	private void calculateCapRadius() {
		if (capCircumference != 0) {
			capRadius = capCircumference / (2 * pi);
			found = true;
		} else if (capHeight != 0) {
			if (capArea != 0) {
				capRadius = sqrt((capArea - (pi * pow(capHeight, 2))) / pi);
				found = true;
			} else if (radius != 0) {
				capRadius = sqrt((2 * capHeight * radius) - pow(capHeight, 2));
				found = true;
			} else if (capVolume != 0) {
				capRadius = sqrt((((6 * capVolume) / (pi * capHeight)) - pow(
						capHeight, 2)) / 3);
				found = true;
			}
		}
	}

	private void calculateCapCircumference() {
		if (capRadius != 0) {
			capCircumference = 2 * pi * capRadius;
			found = true;
		}
	}

	private void calculateCapArea() {
		if (capHeight != 0) {
			if (radius != 0) {
				capArea = 2 * pi * radius * capHeight;
				found = true;
			} else if (capRadius != 0) {
				capArea = pi * (pow(capRadius, 2) + pow(capHeight, 2));
				found = true;
			}
		}
	}

	private void calculateCapVolume() {
		if (capHeight != 0 && capRadius != 0) {
			capVolume = ((pi * capHeight) / 6)
					* ((3 * pow(capRadius, 2)) + pow(capHeight, 2));
			found = true;
		}
	}

	private void calculateRadius() {
		if (area != 0) {
			radius = sqrt(area / (4 * pi));
			found = true;
		} else if (circumference != 0) {
			radius = circumference / (2 * pi);
			found = true;
		} else if (volume != 0) {
			radius = pow(((3 * volume) / (4 * pi)), 1 / 3);
			found = true;
		} else if (capHeight != 0) {
			if (capArea != 0) {
				radius = capArea / (2 * pi * capHeight);
				found = true;
			} else if (capRadius != 0) {
				radius = (pow(capRadius, 2) + pow(capHeight, 2))
						/ (2 * capHeight);
				found = true;
			}
		}
	}

	private void calculateCircumference() {
		if (radius != 0) {
			circumference = 2 * pi * radius;
			found = true;
		}
	}

	private void calculateArea() {
		if (radius != 0) {
			area = 4 * pi * pow(radius, 2);
			found = true;
		}
	}

	private void calculateVolume() {
		if (radius != 0) {
			volume = (4 * pi * pow(radius, 3)) / 3;
			found = true;
		}
	}

}
